package com.app.Timer;

import com.app.model.cache.RedisKeys;
import com.app.model.main.*;
import com.app.model.position.Position;
import com.app.model.position.PositionFlag;
import com.app.model.position.UserNearBy;
import com.app.model.solr.SolrFlag;
import com.app.model.util.ArticleFlag;
import com.app.model.util.CommonFlag;
import com.app.model.util.HotArticle;
import com.app.model.util.UserWithScore;
import com.app.repository.interfaces.*;
import com.app.service.PositionService;
import com.app.service.SolrService;
import com.app.service.utils.RecommendUtil;
import com.app.support.utils.RedisUtil;
import org.apache.log4j.Logger;
import org.apache.solr.common.SolrDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * Created by Lichaojie on 2017/3/26. It's a beautiful day.
 */
@Component
public class RecommendTimer {

    private final Logger logger = Logger.getLogger(RecommendTimer.class);

    @Autowired
    private ArticleMapper articleMapper;//文章
    @Autowired
    private ApprovalMapper approvalMapper;//点赞数
    @Autowired
    private CommentsOfArticleMapper commentsOfArticleMapper; //文章评论数
    @Autowired
    private ScanRecordMapper scanRecordMapper; //浏览记录
    @Autowired
    private RedisUtil redisUtil; //??
    @Autowired
    private LoginRecordMapper loginRecordMapper; //登录记录
    @Autowired
    private UserMapper userMapper; //用户
    @Autowired
    private SolrService solrService; //搜索记录
    @Autowired
    private UserInfoMapper userInfoMapper; //用户信息
    @Autowired
    private PositionService positionService;  //位置服务
    @Autowired
    private RecommendUtil recommendUtil;  //推荐

    private final int MAX_RECOMMEND_USER_NUMBER = 1000;


    @Scheduled(cron = "00 00 00 * * ?")
    public void hotArticleTimer(){

        List<HotArticle> hotArticleList;

        long time2 = System.currentTimeMillis();
        long time1 = time2 - 7*24*60*60*1000;

        //7天内的帖子
        List<Article> idList1;
        //7天前的帖子
        List<Article> idList2;
        List<HotArticle> hotArticleList1,hotArticleList2;

        idList1 = articleMapper.getIdListBetween(time1 + "",time2 + "", ArticleFlag.SHARE);
        int num = idList1.size();
        if(num < 1000){
            idList2 = articleMapper.getListOrderedByTimeWithoutUserIdDESC(time1 + "",0,2000,ArticleFlag.SHARE);
        }else if( num >= 1000 && num < 2000){
            idList2 = articleMapper.getListOrderedByTimeWithoutUserIdDESC(time1 + "",0,1000,ArticleFlag.SHARE);
        }else if(num >= 2000 && num < 3000){
            idList2 = null;
        }else{
            idList2 = null;
        }

        hotArticleList1 = new ArrayList<>();
        for (Article article : idList1){
            HotArticle hotArticle = new HotArticle();
            long id = article.getId();
            long approvalNum = approvalMapper.getApprovalNumber(id, CommonFlag.ARTICLE);
            long commentsNum = commentsOfArticleMapper.getCommentsNumber(id);
            long scanNum = scanRecordMapper.getScanNumber(id);

            hotArticle.setApprovalNum(approvalNum)
                    .setCommentsNum(commentsNum)
                    .setScanNum(scanNum)
                    .setId(id)
                    .setHotValue(scanNum + 4 * approvalNum + 16 * commentsNum);
            hotArticleList1.add(hotArticle);
        }


        logger.info("hotArticleList1.size() : " + hotArticleList1.size());

        Collections.sort(hotArticleList1);//将7天内所有帖子按HOT值进行排序

        hotArticleList2 = new ArrayList<>();
        if(idList2 != null){
            for (Article article : idList2){
                HotArticle hotArticle = new HotArticle();
                long id = article.getId();
                long approvalNum = approvalMapper.getApprovalNumber(id, CommonFlag.ARTICLE);
                long commentsNum = commentsOfArticleMapper.getCommentsNumber(id);
                long scanNum = scanRecordMapper.getScanNumber(id);

                hotArticle.setApprovalNum(approvalNum)
                        .setCommentsNum(commentsNum)
                        .setScanNum(scanNum)
                        .setId(id)
                        .setHotValue(scanNum + 4 * approvalNum + 16 * commentsNum);
                hotArticleList2.add(hotArticle);
            }
        }


        logger.info("hotArticleList2.size() : " + hotArticleList2.size());

        Collections.sort(hotArticleList2);

        hotArticleList = new ArrayList<>();
        if(hotArticleList1.size() < 3000){
            for (HotArticle hotArticle : hotArticleList1){
                hotArticleList.add(hotArticle);
            }
            for (HotArticle hotArticle : hotArticleList2){
                hotArticleList.add(hotArticle);
            }
        }else{
            for(int i = 0; i < 3000; i ++){
                hotArticleList.add(hotArticleList1.get(i));
            }
        }

        String key = RedisKeys.RECOMMEND_ARTICLE_LIST;
        if(redisUtil.exists(key)){
            redisUtil.remove(key);
        }
        redisUtil.set(key, hotArticleList);
    }


    /**
     * 每天为每个用户计算一组推荐的用户，最大数目不超过1000
     */
    @Deprecated
    //@Scheduled(cron = "00 00 00 * * ?")
    public void userRecommendTimer(){

        for (long i = 1; i <= userMapper.getMaxId(); i ++){
            computeUserRecommend(i);
        }

    }


    @Deprecated
    //@Scheduled(cron = "00 00 00 * * ?")
    public void userRecommendWithDistanceTime(){
        for (long i = 1; i <= userMapper.getMaxId(); i ++){
            List<Long> list = redisUtil.get(RedisKeys.RECOMMEND_USER_LIST + Long.toString(i));
            if(list == null){
                computeUserRecommend(i);
                list = redisUtil.get(RedisKeys.RECOMMEND_USER_LIST + Long.toString(i));
            }
            computeUserRecommendWithDistance(i, list);
        }
    }

    /**
     * 计算单个用户的推荐用户
     * @param userId
     */
    public void computeUserRecommend(long userId){

        List<LoginRecord> latestUser;//七天内登录记录
        Set<Long> idSet = new HashSet<>();//七天内所有登录的用户ID

        long time2 = System.currentTimeMillis();
        long time1 = time2 - 7*24*60*60*1000;

//        logger.info("time1 : " + time1);
//        logger.info("time2 : " + time2);
        latestUser = loginRecordMapper.getIdListBetween(Long.toString(time1),Long.toString(time2));
        for (LoginRecord loginRecord : latestUser){
            idSet.add(loginRecord.getUserId());
        }
//        logger.info("<--idList--");
//        for (Long id : idSet){
//            logger.info("> : " + id);
//        }
//        logger.info("--idList-->");

        UserInfo userInfo = userInfoMapper.selectByPrimaryKey(userId);
        String labelString = userInfo.getLabel();
        String[] labels = labelString.split(",");
        String keywords= "";
        for (String label : labels){
            keywords += (label + " ");
        }

        List resultList = solrService.query(keywords, SolrFlag.USER,0,2000);

        logger.info("<--resultList--");
        List<Long> list = new ArrayList<>();//与每个用户匹配度最高的2000个用户的ID
        for (Object object : resultList){
            SolrDocument solrDocument = (SolrDocument)object;
            list.add(Long.parseLong((String)solrDocument.getFieldValue("id")));
            //注释掉
            //logger.info("> : " + (String)solrDocument.getFieldValue("id"));
        }
        logger.info("--resultList-->");

        List<Long> tempList = new ArrayList<>();
        tempList.addAll(list);

        for (Long id : tempList){//将匹配度高的用户中最近一周没登录过的用户去除
            if(!idSet.contains(id)){
                list.remove(new Long(id));
            }
        }

        //限制推荐的用户数
        long listSize = list.size();
        if(listSize > MAX_RECOMMEND_USER_NUMBER){
            for (int j = MAX_RECOMMEND_USER_NUMBER; j < listSize; j ++){
                list.remove(j);
            }
        }

        //去除用户自身
        list.remove(new Long(userId));
        //userRecommendMap.put(userInfo.getId(), list);
        String key = RedisKeys.RECOMMEND_USER_LIST + Long.toString(userId);
        if(redisUtil.exists(key)){
            redisUtil.remove(key);
        }
        redisUtil.set(key, list);

    }


    /**
     * 计算单个用户推荐的附近用户
     * @param userId
     * @param list
     */
    public void computeUserRecommendWithDistance(long userId,List<Long> list){
        UserInfo userInfoI = userInfoMapper.selectByPrimaryKey(userId);
        String positionStr = userInfoI.getPosition();
        String[] strArray = positionStr.split(",");
        Position myPosition = new Position(Double.parseDouble(strArray[0]),Double.parseDouble(strArray[1]));

        List<UserNearBy> userNearByList = new ArrayList<>();
        for (Long id : list){
            UserInfo userInfo = userInfoMapper.selectByPrimaryKey(id);
            String pStr = userInfo.getPosition();
            if(pStr.equals("")){
                continue;
            }
            String[] tStrArray = pStr.split(",");
            Position tPosition = new Position(Double.parseDouble(tStrArray[0]),Double.parseDouble(tStrArray[1]));
            double distance = positionService.getDistance(myPosition, tPosition);
            if(distance - PositionFlag.MAX_DISTANCE < 0){//找到所有在附近的用户
                UserNearBy userNearBy = new UserNearBy();
                userNearBy.setUserId(id)
                        .setDistance(distance);
                userNearByList.add(userNearBy);
            }
        }

        Collections.sort(userNearByList);

        List<Long> resultList = new ArrayList<>();
        for (UserNearBy userNearBy : userNearByList){
            resultList.add(userNearBy.getUserId());
        }

        String key = RedisKeys.RECOMMEND_USER_WITH_DISTANCE_LIST + Long.toString(userId);
        if(redisUtil.exists(key)){
            redisUtil.remove(key);
        }
        redisUtil.set(key,resultList);
    }

    /**
     * 计算所有用户的推荐用户，时间间隔 2小时
     */
    //2018.5.8 当前版本有效，测试时关闭
    @Scheduled(fixedRate = 2*60*60*1000)
    public void computeAllRecommend(){
        logger.info("计算推荐用户开始时间 ： " + new Date().toString());
        long maxId = userInfoMapper.getMaxId();
        for (long i = 1; i <= maxId; i ++){
            UserInfo userInfo = userInfoMapper.selectByPrimaryKey(i);
            if(!userInfo.getLabel().equals("")){
                computeSingleRecommend(i);
            }
            logger.info("计算用户【 " + i + " 】的推荐用户…");
        }
        logger.info("计算推荐用户结束时间 ： " + new Date().toString());
    }

    /**
     * 计算单个用户的推荐用户
     * @param userId
     */
    public void computeSingleRecommend(long userId){
        String key1 = RedisKeys.RECOMMEND_USER_LIST + Long.toString(userId);
        long nowTime = System.currentTimeMillis();

        List<User> userList = userMapper.getAllUser();
        List<UserWithScore> userWithScoreList = new ArrayList<>();
        for (User user : userList){
            long id = user.getId();
            if(id != userId){//去掉被推荐用户本身
                UserWithScore userWithScore = new UserWithScore();

                double score1 = recommendUtil.getTimeLiness(id, nowTime);
                double score2 = recommendUtil.getSuitability(userId, id);
                double score3 = recommendUtil.getAuthoritative(id);

                userWithScore.setUserId(id)
                        .setScore(score1 + score2 + score3);
                userWithScoreList.add(userWithScore);
            }
        }

        //按得分升序排序
        Collections.sort(userWithScoreList);
        //翻转之后变为得分降序
        Collections.reverse(userWithScoreList);

        redisUtil.set(key1,userWithScoreList);
    }

}
